//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <xllp_dmac.h>
#include <bulverde.h>
#include "ccamera.h"

#if (_TEST_TYPE == 41)      /* Test for Runtime of ISR */
#define SAMPLETIMES 50
UInt32 uTimerStart, uTimerEnd;
UInt32 ulCapturePhotoTime[SAMPLETIMES];
Int32 nCapturePhotoSampleCounter = 0;
#endif //_TEST_TYPE == 41

typedef struct tagRECT
{
    Int32 left;
    Int32 top;
    Int32 right;
    Int32 bottom;
} RECT;

class CCamera : public CDeviceDriver
{
public:
    CARAPI Read(
                    /* [in] */ Int64 u64Offset,
                    /* [in] */ Int32 bytesToRead,
                    /* [out] */ MemoryBuf * pBuffer,
                    /* [out] */ IEvent * * ppCompletionEvent);
    CARAPI Write(
                    /* [in] */ Int64 u64Offset,
                    /* [in] */ const MemoryBuf &buffer,
                    /* [out] */ Int32 * pBytesWritten,
                    /* [out] */ IEvent * * ppCompletionEvent);
    CARAPI Control(
                    /* [in] */ Handle32 nControlCode,
                    /* [in] */ const MemoryBuf &inBuffer,
                    /* [out] */ MemoryBuf * pOutBuffer,
                    /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();
    Boolean   Install();
    DzMutex   m_lock;

public:
    CamDrv_Context CurrentContext;
};

ECode CCamera::Read(
                /* [in] */ Int64 u64Offset,
                /* [in] */ Int32 bytesToRead,
                /* [out] */ MemoryBuf * pBuffer,
                /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CCamera::Write(
                /* [in] */ Int64 u64Offset,
                /* [in] */ const MemoryBuf &buffer,
                /* [out] */ Int32 * pBytesWritten,
                /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

void PoSetCameraPowerOn(Boolean PowerOn);
EXTERN_C Boolean IsOverlayEnable();
ECode CCamera::Control(
                /* [in] */ Handle32 nControlCode,
                /* [in] */ const MemoryBuf &inBuffer,
                /* [out] */ MemoryBuf * pOutBuffer,
                /* [out] */ IEvent * * ppCompletionEvent)
{

    ECode ec = NOERROR;
    WaitResult wr;
    Camera_Format *pFormat;
    //cprintf("+++++++++CCamera::Control %d\n", nControlCode);
    m_lock.Lock(&wr);
    switch (nControlCode) {
        case 0:
            // Initialize camera device
            ec = CamDrv_Init(&CurrentContext);
            break;

        case 1:
            // Quit camera device
            CamDrv_Quit(&CurrentContext);
            CurrentContext.CaptureStatus = CamDrv_Status_Closed;
            break;

        case 2:
            // Get photo information and data
            // If continue snap is set, maybe there are more than 1 photo
#if (_TEST_TYPE == 41)
    uTimerStart = OSCR0;
#endif //_TEST_TYPE == 41
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            if (pOutBuffer->GetCapacity() < (int)sizeof(Camera_Photo)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            if (CurrentContext.CaptureStatus == CamDrv_Status_Preview) {
                CamDrv_StopVideoCapture(&CurrentContext);
            }
//            ec = CamDrv_GetSnapFrames((Camera_Photo *)(Byte *)ebbOutData);
            CurrentContext.CaptureStatus = CamDrv_Status_Still;

            // Start to capture photos and wait here
            ec = CamDrv_StartStillCapture(&CurrentContext);
            // photo data are already in buffer or time is out
            CamDrv_StopStillCapture(&CurrentContext);

            if (FAILED(ec)) break;

            ec = CamDrv_GetStillPhoto(&CurrentContext,
                            (Camera_Photo *)pOutBuffer->GetPayload());
            pOutBuffer->SetUsed(sizeof(Camera_Photo));
#if (_TEST_TYPE == 41)
    uTimerEnd = OSCR0;
    if (nCapturePhotoSampleCounter >= SAMPLETIMES) nCapturePhotoSampleCounter = 0;
    ulCapturePhotoTime[nCapturePhotoSampleCounter++] = \
                            (uTimerEnd >= uTimerStart) ? \
                            (uTimerEnd - uTimerStart) : \
                            ((UInt32)0xFFFFFFFF - uTimerStart + uTimerEnd);
#endif //_TEST_TYPE == 42
            break;

        case 3:
            // Begin preview
            if (inBuffer.GetCapacity() < (int)sizeof(RECT)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            if (CurrentContext.CaptureStatus == CamDrv_Status_Preview) {
                break;
            }
            CurrentContext.CaptureStatus = CamDrv_Status_Preview;
            ec = CamDrv_StartVideoCapture(&CurrentContext);
            PoSetCameraPowerOn(TRUE);//tell power manager not to suspend camera
            break;

        case 4:
            // Stop preview
            if (CurrentContext.CaptureStatus == CamDrv_Status_Preview) {
                CamDrv_StopVideoCapture(&CurrentContext);
            }
            CurrentContext.CaptureStatus = CamDrv_Status_Unprepared;
            PoSetCameraPowerOn(FALSE);//tell power manager to suspend camera
            break;

        case 5:
            // Release the memory of frames, except the default preview buffer.
            CamDrv_ReleaseFrames(&CurrentContext);
            break;

        case 6:
            // Set many formats for camera device, such as photo quality, brightness ...
            if (inBuffer.GetCapacity() < (int)sizeof(Camera_Format)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }

            pFormat = (Camera_Format *)inBuffer.GetPayload();

            ec = CamDrv_SetAppFormat(&CurrentContext, pFormat->nID,
                    pFormat->wParam, pFormat->lParam);
            break;

        case 7:
            // Get many formats for camera device, such as photo quality, brightness ...
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            if (inBuffer.GetCapacity() < (int)sizeof(int) ||
                pOutBuffer->GetCapacity() < (int)sizeof(Camera_Format)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }

            pFormat = (Camera_Format *)pOutBuffer->GetPayload();
            pFormat->nID = *(int *)inBuffer.GetPayload();

            ec = CamDrv_GetAppFormat(&CurrentContext, pFormat->nID,
                    &pFormat->wParam, &pFormat->lParam);
            pOutBuffer->SetUsed(sizeof(Camera_Format));
            break;

        case 8:
            // Enter photo/video mode.
            // Default thhe driver enters photo mode.
            if (inBuffer.GetPayload() == NULL) CurrentContext.bVideo = FALSE;
            else CurrentContext.bVideo = *(int *)inBuffer.GetPayload();

            if (CurrentContext.bVideo) {
                CamDrv_SetVideoResolution(&CurrentContext,
                    CurrentContext.AppFormat.VideoResolution);
            }
            else {
                // Video buffer may be changed from video mode to still mode.
                // And video buffer is const (ALL QVGA) in still mode.
                if (CurrentContext.VideoBuffer.AllocBufSize > 0) {
                    DMAFreeBuffer(&CurrentContext, BUFTYPE_VIDEO);
                }
                CamDrv_SetPhotoResolution(&CurrentContext,
                    CurrentContext.AppFormat.PhotoResolution);
            }
            break;

        case 9: {
            // Begin video record
            if (!CurrentContext.bVideo) {
                ec = E_ACCESS_DENIED;
                break;
            }
            if (NULL == ppCompletionEvent) {
                ec = E_INVALID_ARGUMENT;
                break;
            }
            if (CurrentContext.CaptureStatus == CamDrv_Status_Preview) {
                CamDrv_StopVideoCapture(&CurrentContext);
            }

            CurrentContext.CaptureStatus = CamDrv_Status_Video;
            ec = CamDrv_StartVideoRecord(&CurrentContext, ppCompletionEvent);
            PoSetCameraPowerOn(TRUE);//tell power manager not to suspend camera
            break;
        }

        case 10:
            // Stop video record
            CurrentContext.CaptureStatus = CamDrv_Status_Unprepared;
            if (!CurrentContext.bVideo) {
                ec = E_ACCESS_DENIED;
                break;
            }
            CamDrv_StopVideoRecord(&CurrentContext);
            CurrentContext.CaptureStatus = CamDrv_Status_Unprepared;
            PoSetCameraPowerOn(FALSE);//tell power manager to suspend camera
            break;

        case 11: {
            // Get image frame information and data,
            Camera_ImageFrame *pFrame;
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            if (pOutBuffer->GetCapacity() < (int)sizeof(Camera_ImageFrame)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            pFrame = (Camera_ImageFrame *)pOutBuffer->GetPayload();
            ec = CamDrv_GetVideoFrame(&CurrentContext, pFrame);

            pOutBuffer->SetUsed(sizeof(Camera_ImageFrame));
            break;
        }

        // Power management code
        case 1000:
            // In preview, still, video mode, no need to close camera.
            if (CurrentContext.CaptureStatus == CamDrv_Status_Unprepared) {
                CamDrv_SaveFormat(&CurrentContext);
                CamDrv_CloseHardware(&CurrentContext);
                // Set the special status for power management.
                CurrentContext.CaptureStatus = CamDrv_Status_Paused;
            }
            break;

        case 1001:
        case 1002:
            if (CurrentContext.CaptureStatus == CamDrv_Status_Paused) {
                CamDrv_SetDefaultFormat(&CurrentContext);
                CamDrv_OpenHardware(&CurrentContext);
                CamDrv_ResumeFormat(&CurrentContext);
                CurrentContext.CaptureStatus = CamDrv_Status_Unprepared;
            }
            break;

#if (_TEST_TYPE == 41)
        case 4100:
            memcpy(pOutBuffer->GetPayload(),
                    ul_DmaChYIsrTime,
                    SAMPLETIMES * sizeof(UInt32));
            pOutBuffer->SetUsed(SAMPLETIMES * sizeof(UInt32));
            ec = NOERROR;
            break;
        case 4101:
            memcpy(pOutBuffer->GetPayload(),
                    ul_DmaChCbIsrTime,
                    SAMPLETIMES * sizeof(UInt32));
            pOutBuffer->SetUsed(SAMPLETIMES * sizeof(UInt32));
            ec = NOERROR;
            break;
        case 4102:
            memcpy(pOutBuffer->GetPayload(),
                    ul_DmaChCrIsrTime,
                    SAMPLETIMES * sizeof(UInt32));
            pOutBuffer->SetUsed(SAMPLETIMES * sizeof(UInt32));
            ec = NOERROR;
            break;

        case 4103:
            memcpy(pOutBuffer->GetPayload(),
                    ulCapturePhotoTime,
                    SAMPLETIMES * sizeof(UInt32));
            pOutBuffer->SetUsed(SAMPLETIMES * sizeof(UInt32));
            ec = NOERROR;
            break;
#endif // _TEST_TYPE 41

        default :
            ec = E_NOT_IMPLEMENTED;
            break;
    }

    m_lock.Unlock();

    return ec;
}

void CCamera::Dispose()
{
    delete this;
}

Boolean CCamera::Install()
{
    // Clear current camera context
    memset(&CurrentContext, 0, sizeof(CamDrv_Context));

    CurrentContext.CaptureStatus = CamDrv_Status_Closed;

    if (FAILED(CamDrv_Install(&CurrentContext))) return FALSE;
    return TRUE;
}

EXTERN IDeviceDriver * CDECL CreateCamera(uint_t uDeviceNo, void *pvParameter)
{
    CCamera *pCamera = new CCamera;

    if (!pCamera) goto ErrorExit;
    if (!pCamera->Install()) goto ErrorExit;

    cprintf("Add Camera OK!\n");

    return pCamera;

ErrorExit:
    cprintf("Add Camera Failed!\n");
    return NULL;
}
